package com.zhdl.hardware.io.smartLink.service.impl;

import cn.hutool.core.util.HexUtil;
import com.project.common.util.DelayUtil;
import com.zhdl.hardware.io.smartLink.repository.IOCmdHandleDao;
import com.zhdl.hardware.io.smartLink.service.IOCmdSendService;
import com.zhdl.hardware_modules.entity.DeviceAction;
import com.zhdl.hardware_modules.repository.DeviceActionRepository;
import com.zhdl.modules.tools.CommonCallback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ScheduledFuture;

@Slf4j
@Service
public class IOCmdSendServiceImpl implements IOCmdSendService {

    /**
     * 线程管理
     */
    Map<String, ScheduledFuture<?>> mapSF = new HashMap<>();

    @Resource
    public ThreadPoolTaskScheduler taskScheduler1;

    @Resource
    public IOCmdHandleDao ioCmdHandleDao;

    @Resource
    private DeviceActionRepository deviceActionRepository;

    @Override
    public DeviceAction findByDeviceId(int deviceId) {
        Optional<DeviceAction> byId = deviceActionRepository.findById(deviceId);
        return byId.orElse(null);
    }

    @Override
    public void updateByDeviceId(int c1, int c2, int c3, int c4) {
        Optional<DeviceAction> byId = deviceActionRepository.findById(1);
        if (byId.isPresent()) {
            byId.get().setC1(c1);
            byId.get().setC2(c2);
            byId.get().setC2(c3);
            byId.get().setC2(c4);
            deviceActionRepository.save(byId.get());
        }
    }

    /**
     * 将byte转换为一个长度为8的byte数组，数组每个值代表bit
     */
    public static Byte[] getBitArray(byte b) {
        Byte[] array = new Byte[8];
        for (int i = 7; i >= 0; i--) {
            array[i] = (byte) (b & 1);
            b = (byte) (b >> 1);
        }
        return array;
    }

    /**
     * 读取IO列表所有信号
     * @param callback 回调
     */
    @Override
    public void readAllCoil02(String ioNum, CommonCallback<List<Byte>> callback){
        taskScheduler1.scheduleWithFixedDelay(() ->
                ioCmdHandleDao.ioReadDiscreteSignal(ioNum,bytes -> {
                    /* 获取当前指令返回的数据 */
                    /* 对返回的数据进行处理 - 每八位进行高低位反转 */
                    List<Byte> ioList = new ArrayList<>();
                    for (byte b : bytes) {
                        Byte[] bitArray = getBitArray(b);
                        List<Byte> listB = Arrays.asList(bitArray);
                        Collections.reverse(listB);
                        ioList.addAll(listB);
                    }
                    callback.statusCallback(ioList);
                    log.info("IO02反转后:"+ ioList);
                }),100);
    }
    /**
     * 读取IO列表所有信号
     * @param callback 回调
     */
    @Override
    public void readAllCoil01(String ioNum, CommonCallback<List<Byte>> callback){
        ioCmdHandleDao.ioReadCoilSignal(ioNum,bytes -> {
                    /* 获取当前指令返回的数据 */
                    /* 对返回的数据进行处理 - 每八位进行高低位反转 */
                    List<Byte> ioList = new ArrayList<>();
                    for (byte b : bytes) {
                        Byte[] bitArray = getBitArray(b);
                        List<Byte> listB = Arrays.asList(bitArray);
                        Collections.reverse(listB);
                        ioList.addAll(listB);
                    }
                    callback.statusCallback(ioList);
                    log.info("IO01反转后:"+ ioList);
                });
    }

    /**
     * IO读取01-传感器状态
     *
     * @param sensorKey key键
     * @param value     预期结果
     * @param timeout   超时时间
     * @param callback  回调
     */
    @Override
    public void ioReadCoil01(String ioNum, int sensorKey, int value, int timeout, CommonCallback<String> callback) {
        /* 声明循环次数 */
        final int[] times = new int[]{0};
        /* 声明scheduledFuture的key */
        String key = "io01" + sensorKey;
        ScheduledFuture<?> scheduledFuture = taskScheduler1.scheduleWithFixedDelay(() ->
                ioCmdHandleDao.ioReadCoilSignal(ioNum,bytes ->
                        ioHandle(times, key, bytes, sensorKey, value, timeout, callback)), 100);
        mapSF.put(key, scheduledFuture);

    }

    /**
     * IO读取02-传感器状态
     *
     * @param sensorKey 传感器地址
     * @param value     预期结果
     * @param timeout   超时时间
     * @param callback  回调
     */
    @Override
    public void ioReadCoil02(String ioNum, int sensorKey, int value, int timeout, CommonCallback<String> callback) {
        /* 声明循环次数 */
        final int[] times = new int[]{0};
        /* 声明scheduledFuture的key */
        String key = "io02" + System.currentTimeMillis();
        ScheduledFuture<?> scheduledFuture = taskScheduler1.scheduleWithFixedDelay(() ->
                ioCmdHandleDao.ioReadDiscreteSignal(ioNum,bytes ->
                        ioHandle(times, key, bytes, sensorKey, value, timeout, callback)), 100);
        mapSF.put(key, scheduledFuture);
    }

    /**
     * IO写入线圈05
     */
    @Override
    public void ioWriteCoil05(int address, boolean open, CommonCallback<String> callback){

        String openStr = open ? "FF00" : "0000";
        ioCmdHandleDao.ioWriteCoilSignal(address, openStr, bytes -> {
            String s = HexUtil.encodeHexStr(bytes);
            log.info(s);
            callback.statusCallback("0");
        });
    }
    /**
     * IO写入线圈05 - 带超时功能
     */
    @Override
    public void ioWriteCoil05(int address, boolean open, CommonCallback<String> callback,int delayTime){
        String openStr = open ? "FF00" : "0000";
        ioCmdHandleDao.ioWriteCoilSignal(address, openStr, bytes -> {
            String s = HexUtil.encodeHexStr(bytes);
            DelayUtil.delayedExecution(delayTime, () -> {
                log.info(s);
                callback.statusCallback("0");
            });
        });
    }
    /**
     * @param times     循环次数
     * @param key       map中存放的key
     * @param bytes     after中获取的结果
     * @param sensorKey 传感器地址
     * @param value     预期结果
     * @param timeout   超时时间
     * @param callback  回调
     */
    private void ioHandle(int[] times, String key, byte[] bytes, int sensorKey, int value, int timeout, CommonCallback<String> callback) {
        /* 获取当前指令返回的数据 */
        /* 对返回的数据进行处理 - 每八位进行高低位反转 */
        List<Byte> ioList = new ArrayList<>();
        for (byte b : bytes) {
            Byte[] bitArray = getBitArray(b);
            List<Byte> listB = Arrays.asList(bitArray);
            Collections.reverse(listB);
            ioList.addAll(listB);
        }
        //打印出 IO 打开列表
        log.info("========当前为" + key + "线圈信号=======");
        for (int i = 0; i < ioList.size(); i++) {
            Byte b = ioList.get(i);
            if (b == 0) {
                System.out.println("第" + i + "位，未打开");
            } else {
                System.out.println("第" + i + "位，当前打开");
            }
        }
        Byte sensorStatus = ioList.get(sensorKey);
        log.info("传感器信号-判断当前:-" + sensorKey +
                "-传感器信号,结果为:-" + sensorStatus +
                "-,预期结果为:" + value);
        if (sensorStatus == value) {
            callback.statusCallback("0");
            ScheduledFuture<?> scheduledFuture00 = mapSF.get(key);
            scheduledFuture00.cancel(false);
            mapSF.remove(key);
        } else {
            /* 超时报警 */
            times[0]++;
            if (times[0] > timeout) {
                ScheduledFuture<?> scheduledFuture00 = mapSF.get(key);
                scheduledFuture00.cancel(false);
                mapSF.remove(key);
                log.info("已经超时，报警信号:" + key);
            }
        }
    }

}
